package com.youhome.housing.config;

import com.youhome.common.response.Result;
import com.youhome.common.response.ResultCode;
import com.youhome.service.service.AuthenticationCompanyTokenService;
import com.youhome.utils.JsonUtils;
import com.youhome.utils.TokenUtils;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.MalformedJwtException;
import io.jsonwebtoken.SignatureException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.util.Map;

/**
 * @Auther: Tangzhiqiang
 * @Date: 2019/1/13 21:18
 * @Description: OncePerRequestFilter 确保在一次请求只通过一次filter，而不需要重复执行。
 */
// TODO 还要实现 token 缓存
@Component
@Slf4j
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {

    @Autowired
    private AuthenticationCompanyTokenService authenticationCompanyTokenService;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {

        String header = request.getHeader("Authorization");
        Boolean isFilter = true;
        if (!StringUtils.isEmpty(header)) {
            String token = header;//header.substring("Bearer ".length());
            System.err.println("token:" + token);
            Long tokenId = null;
            Result result = null;
            try {
                Map<String, Object> claims = TokenUtils.parserToken(token);
                String object = claims.get("TokenId").toString();
                tokenId = Long.valueOf(object);
            } catch (NullPointerException e) {
                log.error("NullPointerException", e);
                result = new Result(ResultCode.INVALID_TOKEN_FORMAT);
            } catch (MalformedJwtException e) {
                log.error("an error occured during getting username from token", e);
                result = new Result(ResultCode.INVALID_TOKEN_FORMAT);
            } catch (ExpiredJwtException e) {
                log.warn("the token is expired and not valid anymore", e);
                result = new Result(ResultCode.INVALID_TOKEN_EXPIRED);
            } catch (SignatureException e) {
                log.error("Authentication Failed. Username or Password not valid.");
                result = new Result(ResultCode.INVALID_TOKEN_SIGNATURE);
            }

            if (tokenId != null && SecurityContextHolder.getContext().getAuthentication() == null) {
                UserDetails userDetails = authenticationCompanyTokenService.loadUserByUsername(tokenId);
                if(!userDetails.isEnabled()){
                    isFilter = false;
                    result = new Result(ResultCode.INVALID_TOKEN_FORBIDDEN);
                }
                if (TokenUtils.isExpired(token)) {
                    UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
                    authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                    SecurityContextHolder.getContext().setAuthentication(authentication);
                } else {
                    isFilter = false;
                    result = new Result(ResultCode.INVALID_TOKEN);
                }
            } else {
                isFilter = false;
                result = new Result(ResultCode.INVALID_TOKEN_EXPIRED);
            }

            if (result != null && result.getStatus() > 0) {// 验证失败
                try {
                    response.setCharacterEncoding("utf-8");
                    response.setContentType("application/json; charset=utf-8");
                    PrintWriter out = response.getWriter();
                    out.print(JsonUtils.bean2Json(result));
                    out.flush();
                    out.close();
                } catch (UnsupportedEncodingException e) {
                    logger.error("过滤器返回信息失败:" + e.getMessage(), e);
                } catch (IOException e) {
                    logger.error("过滤器返回信息失败:" + e.getMessage(), e);
                }
                return;
            }
        }
        if (isFilter) {
            chain.doFilter(request, response);
        }
    }
}
